package vg.modules.filter;


import java.awt.Component;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.util.*;

import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JSplitPane;
import javax.swing.SwingUtilities;
import javax.swing.WindowConstants;

import vg.modules.filter.components.ActionPanel;
import vg.services.data_base_manager.data.graph.Attribute;
import vg.services.data_base_manager.data.graph.Edge;
import vg.services.data_base_manager.data.graph.Graph;
import vg.services.data_base_manager.data.graph.Vertex;
import vg.services.graph_view_manager.interfaces.graph_view.AGraphView;
import vg.services.graph_view_manager.interfaces.graph_view.IGraphRenderer;
import vg.services.graph_view_manager.realization.graph_view.smart_graph_view.realization.actions.DefaultActionAdapter;
import vg.services.graph_view_manager.realization.graph_view.smart_graph_view.realization.actions.GetSelectedSubGraph;
import vg.services.main_manager.MainManager;
import vg.services.plugin_manager.event_and_request.request.UIRequestAddTab;
import vg.services.plugin_manager.event_and_request.request.UIRequestReplaceCurrentTab;
import vg.services.plugin_manager.realization.PluginParameter;
import vg.services.user_interface_manager.additional_swing_components.conditionPanel.ConditionalPanel;

/**
 * This class is a filter for vertexes and edges.
 * @author tzolotuhin
 */
public class FilterPanel extends JFrame implements IFilterPanel {
	private static final long serialVersionUID = 1L;

	// init
	private boolean init = false;

	// Big components of interface
    private JCheckBox vertexConditionCheckBox;
    private JCheckBox edgeConditionCheckBox;

	private ConditionalPanel vertexConditionalPanel;
	private ConditionalPanel edgeConditionalPanel;
	//private ActionPanel actionPanel;

	// Context
	private AGraphView currentView = null;

	// Mutex
	private final Object generalMutex = new Object();

	// Modes
	private int actionMode = IFilterPanel.SELECT_MODE;

	//Other
	private final PluginParameter parameter;

	//Window settings
	private final static String DEF_WINDOWS_SIZE_X = "FILTER_PLUGIN_WindowSizeX";
	private final static String DEF_WINDOWS_SIZE_Y = "FILTER_PLUGIN_WindowSizeY";
	private final static String DEF_WINDOWS_POS_X = "FILTER_PLUGIN_WindowPosX";
	private final static String DEF_WINDOWS_POS_Y = "FILTER_PLUGIN_WindowPosY";
	private Integer windowSizeX, windowSizeY;
	private Integer windowPosX, windowPosY;
	/**
	 * Constructor.
	 * @param title - title of frame.
	 * @param parameter - connection with model and user interface.
	 */
	public FilterPanel(final String title, final PluginParameter parameter) {
		super(title);
		// save input parameters
		this.parameter = parameter;
	}
	/**
	 * This method changes current view.
	 * @param graphView - current view.
	 */
	public void changeView(final AGraphView graphView) {
		synchronized (generalMutex) {
			currentView = graphView;
			if(graphView == null) {
				vertexConditionalPanel.setAttributeSets(null, null);
				edgeConditionalPanel.setAttributeSets(null, null);
				return;
			}

			// find all attributes in current selection subgraph
			graphView.addAction(new GetSelectedSubGraph(GetSelectedSubGraph.GET_COPY_SELECTED_SUBGRAPH_MODE) {
                @Override
                public void finishAction(IGraphRenderer graphRenderer) {
                    Graph ssg = getSelectedSubGraph();
                    if (ssg == null) {
                        vertexConditionalPanel.setAttributeSets(null, null);
                        edgeConditionalPanel.setAttributeSets(null, null);
                        return;
                    }

                    // VERTICES
                    {
                        Set<String> stringAttributeSet = new HashSet<String>();
                        Set<String> realAttributeSet = new HashSet<String>();
                        for (Vertex vertex : ssg.getVertices()) {
                            List<Attribute> attributes = vertex.getAttributes();
                            for (Attribute attribute : attributes) {
                                stringAttributeSet.add(attribute.getName());

                                if (attribute.isRealType()) {
                                    realAttributeSet.add(attribute.getName());
                                }
                            }
                        }
                        vertexConditionalPanel.setAttributeSets(stringAttributeSet, realAttributeSet);
                    }

                    // EDGES
                    {
                        Set<String> stringAttributeSet = new HashSet<String>();
                        Set<String> realAttributeSet = new HashSet<String>();
                        for (Edge edge : ssg.getEdges()) {
                            List<Attribute> attributes = edge.getAttributes();
                            for (Attribute attribute : attributes) {
                                stringAttributeSet.add(attribute.getName());

                                if (attribute.isRealType()) {
                                    realAttributeSet.add(attribute.getName());
                                }
                            }
                        }
                        edgeConditionalPanel.setAttributeSets(stringAttributeSet, realAttributeSet);
                    }
                }
            });
		}
	}

    public void setActionMode(int aMode) {
		synchronized (this.generalMutex) {
			actionMode = aMode;
		}
	}
	
	public void open() {
		synchronized (generalMutex) {
			if(!init) {
				if(SwingUtilities.isEventDispatchThread()) {
					init();
					this.setVisible(true);
				} else {
					SwingUtilities.invokeLater(new Runnable() {
						public void run() {
							synchronized (generalMutex) {
								init();
								FilterPanel.this.setVisible(true);
							}
						}
					});
				}
			} else {
				if(SwingUtilities.isEventDispatchThread()) {
					this.setVisible(true);
				} else {
					SwingUtilities.invokeLater(new Runnable() {
						public void run() {
							synchronized (generalMutex) {
								FilterPanel.this.setVisible(true);
							}
						}
					});
				}				
			}
		}
	}
	
	/**
	 * This method updates ui theme of filter plugin.
	 */
	public void updateUITheme() {
		synchronized (generalMutex) {
			if(init) {
				SwingUtilities.invokeLater(new Runnable() {
					public void run() {
						SwingUtilities.updateComponentTreeUI(FilterPanel.this);
					}
				});
				this.vertexConditionalPanel.updateUITheme();
				this.edgeConditionalPanel.updateUITheme();
			}
		}
	}

//==============================================================================
//-----------------PRIVATE METHODS----------------------------------------------
	private void init() {
		init = true;

		vertexConditionalPanel = new ConditionalPanel();
		edgeConditionalPanel = new ConditionalPanel();

        vertexConditionCheckBox = new JCheckBox("Conditions for vertices:");
        vertexConditionCheckBox.setSelected(true);
        edgeConditionCheckBox = new JCheckBox("Conditions for edges:");
		edgeConditionCheckBox.setSelected(true);

		final ActionPanel actionPanel = new ActionPanel(this);

		// creating of window
		this.loadWindowOptions();
		this.setSize(this.windowSizeX, this.windowSizeY);
		this.setLocation(this.windowPosX, this.windowPosY);
		this.setDefaultCloseOperation(WindowConstants.HIDE_ON_CLOSE);
		this.setLayout(new GridBagLayout());
		this.addComponentListener(new ComponentAdapter() {
			public void componentResized(ComponentEvent e) {
				super.componentResized(e);
			    Component c = (Component)e.getSource();
		        Integer sizeX = c.getSize().width;
		        Integer sizeY = c.getSize().height;
		        MainManager.config.setProperty(DEF_WINDOWS_SIZE_X, sizeX.toString());
		        MainManager.config.setProperty(DEF_WINDOWS_SIZE_Y, sizeY.toString());
			}
			public void componentMoved(ComponentEvent e) {
				super.componentMoved(e);
			    Component c = (Component)e.getSource();
		        Integer posX = c.getLocation().x;
		        Integer posY = c.getLocation().y;
		        MainManager.config.setProperty(DEF_WINDOWS_POS_X, posX.toString());
		        MainManager.config.setProperty(DEF_WINDOWS_POS_Y, posY.toString());
			}
		});

		// package of interface
		// top panel in split pane
		JPanel newLeftComponent = new JPanel(new GridBagLayout());
        newLeftComponent.add(vertexConditionCheckBox, new GridBagConstraints(0,0,  1,1,  0,0,  GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(0, 5, 0, 0),  0,0));
        newLeftComponent.add(vertexConditionalPanel.getView(), new GridBagConstraints(0,1,  1,1,  1,1,  GridBagConstraints.CENTER, GridBagConstraints.BOTH, new Insets(5, 5, 5, 5),  0,0));

		// bottom panel in split pane
		JPanel newRightComponent = new JPanel(new GridBagLayout());
        newRightComponent.add(edgeConditionCheckBox, new GridBagConstraints(0,0,  1,1,  0,0,  GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(0, 5, 0, 0),  0,0));
        newRightComponent.add(edgeConditionalPanel.getView(), new GridBagConstraints(0,1,  1,1,  1,1,  GridBagConstraints.CENTER, GridBagConstraints.BOTH, new Insets(5, 5, 5, 5),  0,0));

		// create split pane
		final JSplitPane vertexAndEdgeSplitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT, newLeftComponent, newRightComponent);
		vertexAndEdgeSplitPane.setResizeWeight(0.5);
		vertexAndEdgeSplitPane.setDividerSize(8);

		add(vertexAndEdgeSplitPane, new GridBagConstraints(0,0,  2,1,  1,1,  GridBagConstraints.CENTER, GridBagConstraints.BOTH, new Insets(5, 5, 5, 5),  0,0));
		add(actionPanel.getView(), new GridBagConstraints(0,1,  2,1,  0,0,  GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets(5, 5, 5, 5),  0,0));
		
		final JButton cancelButton = new JButton("Cancel");
		cancelButton.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				SwingUtilities.invokeLater(new Runnable() {
					public void run() {
						FilterPanel.this.setVisible(false);
					}
				});
			}
		});

        add(cancelButton, new GridBagConstraints(0,2,  1,1,  0,0,  GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets(5, 5, 5, 5),  0,0));

        final JButton actionButton = new JButton("Do");
		actionButton.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				action();
			}
		});
		add(actionButton, new GridBagConstraints(1,2,  1,1,  0,0,  GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets(5, 5, 5, 5),  0,0));
	}
	/**
	 * This method creates new tab with out subgraph.
	 */
	private void action() {
		synchronized (generalMutex) {
			// search id of graph in model
			if(currentView != null) {
                currentView.addAction(new DefaultActionAdapter("filter action") {
                    @Override
                    public void action(final IGraphRenderer parentGraphRenderer) {
                        int graph_id = parentGraphRenderer.getDataBaseGraphId();

                        // get vertex ids and edge ids from database

                        final Set<Integer> vertexIds = vertexConditionCheckBox.isSelected() ? DataBaseWorker4Filter.getVertexIds(vertexConditionalPanel.getConditionTokens(), parameter.graphDataBase, graph_id) : Collections.<Integer>emptySet();
                        final Set<Integer> edgeIds = edgeConditionCheckBox.isSelected() ? DataBaseWorker4Filter.getEdgeIds(edgeConditionalPanel.getConditionTokens(), parameter.graphDataBase, graph_id) : Collections.<Integer>emptySet();

                        switch(actionMode) {
                            case OPEN_IN_NEW_TAB_MODE: {
                                final AGraphView igv = MainManager.graphViewManager.newGraphView(parentGraphRenderer.getCopyGraph());
                                igv.addAction(new DefaultActionAdapter("filter action") {
                                    @Override
                                    public void finishAction(IGraphRenderer graphRenderer) {
                                        parameter.userInterfaceManager.sendRequest(new UIRequestAddTab(igv));
                                    }

                                    @Override
                                    public void action(final IGraphRenderer newGraphRenderer) {
                                        // get vertices and edges
                                        final Set<Vertex> selectedVertices = new HashSet<Vertex>();
                                        final Set<Edge> selectedEdges = new HashSet<Edge>();

                                        final Graph originGraph = newGraphRenderer.getOriginGraph();
                                        final Collection<Vertex> vertices = originGraph.getVertices();
                                        final Collection<Edge> edges = originGraph.getEdges();

                                        if (vertexIds != null && vertices != null) {
                                            for (Vertex vertex : vertices) {
                                                if (vertexIds.contains(vertex.getStorableId())) {
                                                    selectedVertices.add(vertex);
                                                }
                                            }
                                        }

                                        if (edgeIds != null && edges != null) {
                                            for (Edge edge : edges) {
                                                if (edgeIds.contains(edge.getStorableId())) {
                                                    selectedEdges.add(edge);
                                                }
                                            }
                                        }

                                        newGraphRenderer.setInvisibleAllVerticesBut(selectedVertices);
                                        newGraphRenderer.setInvisibleAllEdgesBut(selectedEdges);
                                        newGraphRenderer.refreshView();
                                    }
                                });
                                break;
                            }
                            case OPEN_IN_CURRENT_TAB_MODE: {
                                final AGraphView igv = MainManager.graphViewManager.newGraphView(parentGraphRenderer.getCopyGraph());
                                igv.addAction(new DefaultActionAdapter("filter action") {
                                    @Override
                                    public void finishAction(IGraphRenderer graphRenderer) {
                                        parameter.userInterfaceManager.sendRequest(new UIRequestReplaceCurrentTab(igv));
                                    }

                                    @Override
                                    public void action(final IGraphRenderer newGraphRenderer) {
                                        // get vertices and edges
                                        final Set<Vertex> selectedVertices = new HashSet<Vertex>();
                                        final Set<Edge> selectedEdges = new HashSet<Edge>();

                                        final Graph originGraph = newGraphRenderer.getOriginGraph();
                                        final Collection<Vertex> vertices = originGraph.getVertices();
                                        final Collection<Edge> edges = originGraph.getEdges();

                                        if (vertexIds != null && vertices != null) {
                                            for (Vertex vertex : vertices) {
                                                if (vertexIds.contains(vertex.getStorableId())) {
                                                    selectedVertices.add(vertex);
                                                }
                                            }
                                        }

                                        if (edgeIds != null && edges != null) {
                                            for (Edge edge : edges) {
                                                if (edgeIds.contains(edge.getStorableId())) {
                                                    selectedEdges.add(edge);
                                                }
                                            }
                                        }


                                        newGraphRenderer.setInvisibleAllVerticesBut(selectedVertices);
                                        newGraphRenderer.setInvisibleAllEdgesBut(selectedEdges);
                                        newGraphRenderer.refreshView();
                                    }
                                });
                                break;
                            }
                            case SELECT_MODE: {
                                // get vertices and edges
                                final Set<Vertex> selectedVertices = new HashSet<Vertex>();
                                final Set<Edge> selectedEdges = new HashSet<Edge>();

                                final Graph originGraph = parentGraphRenderer.getOriginGraph();
                                final Collection<Vertex> vertices = originGraph.getVertices();
                                final Collection<Edge> edges = originGraph.getEdges();

                                if (vertexIds != null && vertices != null) {
                                    for (Vertex vertex : vertices) {
                                        if (vertexIds.contains(vertex.getStorableId())) {
                                            selectedVertices.add(vertex);
                                        }
                                    }
                                }

                                if (edgeIds != null && edges != null) {
                                    for (Edge edge : edges) {
                                        if (edgeIds.contains(edge.getStorableId())) {
                                            selectedEdges.add(edge);
                                        }
                                    }
                                }

                                parentGraphRenderer.selectElements(selectedVertices, selectedEdges);
                                break;
                            }
                        }
                    }
                });
            }
		}
	}
	
	/**
	 * Load window options.
	 */
	private void loadWindowOptions() {
		String wSizeStrX = MainManager.config.getProperty(DEF_WINDOWS_SIZE_X);
		String wSizeStrY = MainManager.config.getProperty(DEF_WINDOWS_SIZE_Y);
		String wPosStrX = MainManager.config.getProperty(DEF_WINDOWS_POS_X);
		String wPosStrY = MainManager.config.getProperty(DEF_WINDOWS_POS_Y);
		//set size X(width of window)
		if(wSizeStrX == null) {
			this.windowSizeX = 800;
			MainManager.config.setProperty(DEF_WINDOWS_SIZE_X, this.windowSizeX.toString());
		} else {
			try {
				this.windowSizeX = new Integer(wSizeStrX);
			} catch(NumberFormatException ex) {
				this.windowSizeX = 800;
				MainManager.config.setProperty(DEF_WINDOWS_SIZE_X, this.windowSizeX.toString());
			}
		}
		//set size Y(height of window)
		if(wSizeStrY == null) {
			this.windowSizeY = 600;
			MainManager.config.setProperty(DEF_WINDOWS_SIZE_Y, this.windowSizeY.toString());			
		} else {
			try {
				this.windowSizeY = new Integer(wSizeStrY);
			} catch(NumberFormatException ex) {
				this.windowSizeY = 600;
				MainManager.config.setProperty(DEF_WINDOWS_SIZE_Y, this.windowSizeY.toString());
			}			
		}
		//set position X
		if(wPosStrX == null) {
			this.windowPosX = 200;
			MainManager.config.setProperty(DEF_WINDOWS_POS_X, this.windowPosX.toString());
		} else {
			try {
				this.windowPosX = new Integer(wPosStrX);
			} catch(NumberFormatException ex) {
				this.windowPosX = 800;
				MainManager.config.setProperty(DEF_WINDOWS_POS_X, this.windowPosX.toString());
			}
		}
		//set position Y
		if(wPosStrY == null) {
			this.windowPosY = 200;
			MainManager.config.setProperty(DEF_WINDOWS_POS_Y, this.windowPosY.toString());
		} else {
			try {
				this.windowPosY = new Integer(wPosStrY);
			} catch(NumberFormatException ex) {
				this.windowPosY = 800;
				MainManager.config.setProperty(DEF_WINDOWS_POS_Y, this.windowPosY.toString());
			}
		}
	} 
}
